]> dgit.raspbian.org Git - gnome-shell.git/commitdiff
messageTray: implement showing images in notifications
authorNeha Doijode <ndoijode@gmail.com>
Mon, 29 Aug 2011 17:11:24 +0000 (22:41 +0530)
committerMarina Zhurakhinskaya <marinaz@redhat.com>
Mon, 29 Aug 2011 17:46:47 +0000 (13:46 -0400)
Images are part of the notification spec, so we should support them.

Marina Zhurakhinskaya provided some code for getting the layout right
for this patch.

https://bugzilla.gnome.org/show_bug.cgi?id=621009

data/theme/gnome-shell.css
js/ui/messageTray.js
js/ui/notificationDaemon.js

index f73abb401199106010036c0725fac41348e18ced..070d082c83e13dbef796f3e75ec1891c842eb058 100644 (file)
@@ -1070,6 +1070,14 @@ StTooltip StLabel {
     padding-bottom: 8px;
 }
 
+/* We use row-span = 2 for the image cell, which prevents its height preferences to be
+   taken into account during allocation, so its height ends up being limited by the height
+   of the content in the other rows. To avoid showing a stretched image, we set the minimum
+   height of the table to be ICON_SIZE + IMAGE_SIZE + spacing-rows = 24 + 125 + 10 = 159 */
+.notification-with-image {
+    min-height: 159px;
+}
+
 .summary-boxpointer {
     -arrow-border-radius: 8px;
     -arrow-background-color: rgba(0,0,0,0.9);
index 5be5fd66a2d4579cc99c6f9fead5a872f6727379..89d3aca4f357129e2570746a6e36a7dd4a481bee 100644 (file)
@@ -396,6 +396,8 @@ function Notification(source, title, banner, params) {
 }
 
 Notification.prototype = {
+    IMAGE_SIZE: 125,
+
     _init: function(source, title, banner, params) {
         this.source = source;
         this.urgency = Urgency.NORMAL;
@@ -412,6 +414,7 @@ Notification.prototype = {
         this._titleDirection = St.TextDirection.NONE;
         this._spacing = 0;
         this._scrollPolicy = Gtk.PolicyType.AUTOMATIC;
+        this._imageBin = null;
 
         source.connect('destroy', Lang.bind(this,
             function (source, reason) {
@@ -441,9 +444,19 @@ Notification.prototype = {
         this._bannerBox.connect('allocate', Lang.bind(this, this._bannerBoxAllocate));
         this._table.add(this._bannerBox, { row: 0,
                                            col: 1,
+                                           col_span: 2,
+                                           x_expand: false,
                                            y_expand: false,
                                            y_fill: false });
 
+        // This is an empty cell that overlaps with this._bannerBox cell to ensure
+        // that this._bannerBox cell expands horizontally, while not forcing the
+        // this._imageBin that is also in col: 2 to expand horizontally.
+        this._table.add(new St.Bin(), { row: 0,
+                                        col: 2,
+                                        y_expand: false,
+                                        y_fill: false });
+
         this._titleLabel = new St.Label();
         this._bannerBox.add_actor(this._titleLabel);
         this._bannerUrlHighlighter = new URLHighlighter();
@@ -495,7 +508,10 @@ Notification.prototype = {
             this._actionArea = null;
             this._buttonBox = null;
         }
-        if (!this._scrollArea && !this._actionArea)
+        if (this._imageBin && params.clear)
+            this.unsetImage();
+
+        if (!this._scrollArea && !this._actionArea && !this._imageBin)
             this._table.remove_style_class_name('multi-line-notification');
 
         this._icon = params.icon || this.source.createNotificationIcon();
@@ -559,7 +575,9 @@ Notification.prototype = {
                                                vscrollbar_policy: this._scrollPolicy,
                                                hscrollbar_policy: Gtk.PolicyType.NEVER,
                                                style_class: 'vfade' });
-        this._table.add(this._scrollArea, { row: 1, col: 1 });
+        this._table.add(this._scrollArea, { row: 1,
+                                            col: 2 });
+        this._updateLastColumnSettings();
         this._contentArea = new St.BoxLayout({ name: 'notification-body',
                                                vertical: true });
         this._scrollArea.add_actor(this._contentArea);
@@ -636,13 +654,49 @@ Notification.prototype = {
         if (!props)
             props = {};
         props.row = 2;
-        props.col = 1;
+        props.col = 2;
 
         this._table.add_style_class_name('multi-line-notification');
         this._table.add(this._actionArea, props);
+        this._updateLastColumnSettings();
         this._updated();
     },
 
+    _updateLastColumnSettings: function() {
+        if (this._scrollArea)
+            this._table.child_set(this._scrollArea, { col: this._imageBin ? 2 : 1,
+                                                      col_span: this._imageBin ? 1 : 2 });
+        if (this._actionArea)
+            this._table.child_set(this._actionArea, { col: this._imageBin ? 2 : 1,
+                                                      col_span: this._imageBin ? 1 : 2 });
+    },
+
+    setImage: function(image) {
+        if (this._imageBin)
+            this.unsetImage();
+        this._imageBin = new St.Bin();
+        this._imageBin.child = image;
+        this._imageBin.opacity = 230;
+        this._table.add_style_class_name('notification-with-image');
+        this._updateLastColumnSettings();
+        this._table.add(this._imageBin, { row: 1,
+                                          col: 1,
+                                          row_span: 2,
+                                          x_expand: false,
+                                          y_expand: false,
+                                          x_fill: false,
+                                          y_fill: false });
+    },
+
+    unsetImage: function() {
+        if (this._imageBin) {
+            this._table.remove_style_class_name('notification-with-image');
+            this._table.remove_actor(this._imageBin);
+            this._imageBin = null;
+            this._updateLastColumnSettings();
+        }
+    },
+
     // addButton:
     // @id: the action ID
     // @label: the label for the action's button
@@ -658,7 +712,9 @@ Notification.prototype = {
 
             let box = new St.BoxLayout({ name: 'notification-actions' });
             this.setActionArea(box, { x_expand: false,
+                                      y_expand: false,
                                       x_fill: false,
+                                      y_fill: false,
                                       x_align: St.Align.END });
             this._buttonBox = box;
         }
index 361c585d7fbd1a49e76dc06bc1eba8ba0b90e1cf..d3321d7100eea06e8be15e697103ea13162e036b 100644 (file)
@@ -119,11 +119,6 @@ NotificationDaemon.prototype = {
                 return new St.Icon({ icon_name: icon,
                                      icon_type: St.IconType.FULLCOLOR,
                                      icon_size: size });
-        } else if (hints['image-data']) {
-            let [width, height, rowStride, hasAlpha,
-                 bitsPerSample, nChannels, data] = hints['image-data'];
-            return textureCache.load_from_raw(data, hasAlpha,
-                                              width, height, rowStride, size);
         } else {
             let stockIcon;
             switch (hints.urgency) {
@@ -220,14 +215,18 @@ NotificationDaemon.prototype = {
 
         hints = Params.parse(hints, { urgency: Urgency.NORMAL }, true);
 
-        // Be compatible with the various hints for image data
-        // 'image-data' is the latest name of this hint, introduced in 1.2
-        if (!hints['image-data']) {
+        // Be compatible with the various hints for image data and image path
+        // 'image-data' and 'image-path' are the latest name of these hints, introduced in 1.2
+
+        if (!hints['image-path'] && hints['image_path'])
+            hints['image-path'] = hints['image_path']; // version 1.1 of the spec
+
+        if (!hints['image-data'])
             if (hints['image_data'])
                 hints['image-data'] = hints['image_data']; // version 1.1 of the spec
-            else if (hints['icon_data'])
-                hints['image-data'] = hints['icon_data']; // previous versions of the spec
-        }
+            else if (hints['icon_data'] && !hints['image-path'])
+                // early versions of the spec; 'icon_data' should only be used if 'image-path' is not available
+                hints['image-data'] = hints['icon_data'];
 
         let ndata = { appName: appName,
                       icon: icon,
@@ -331,6 +330,23 @@ NotificationDaemon.prototype = {
                                                  clear: true });
         }
 
+        if (hints['image-data'] || hints['image-path']) {
+            let image = null;
+            if (hints['image-data']) {
+                let [width, height, rowStride, hasAlpha,
+                 bitsPerSample, nChannels, data] = hints['image-data'];
+                image = St.TextureCache.get_default().load_from_raw(data, hasAlpha,
+                                                                    width, height, rowStride, notification.IMAGE_SIZE);
+            } else if (hints['image-path']) {
+                image = St.TextureCache.get_default().load_uri_async(GLib.filename_to_uri(hints['image-path'], null),
+                                                                     notification.IMAGE_SIZE,
+                                                                     notification.IMAGE_SIZE);
+            }
+            notification.setImage(image);
+        } else {
+            notification.unsetImage();
+        }
+
         if (actions.length) {
             notification.setUseActionIcons(hints['action-icons'] == true);
             for (let i = 0; i < actions.length - 1; i += 2) {